/*
 *    Copyright 2009-2021 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;

import org.apache.ibatis.reflection.invoker.GetFieldInvoker;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.reflection.invoker.MethodInvoker;
import org.apache.ibatis.reflection.property.PropertyTokenizer;

/**
 * 类的元数据
 *
 * @author Clinton Begin
 */
public class MetaClass {

  /**
   * 反射工厂
   */
  private final ReflectorFactory reflectorFactory;

  /**
   * reflector 对象
   */
  private final Reflector reflector;

  /**
   * 构造方法
   *
   * @param type 指定类
   * @param reflectorFactory 反射工厂
   * @author evan Date : 2021/10/11
   */
  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;

    // 通过反射工厂创建 reflector
    this.reflector = reflectorFactory.findForClass(type);
  }

  /**
   * 静态方法创建 MetaClass 对象
   *
   * @author evan Date : 2021/10/11
   */
  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }

  /**
   * 获取类属性的 metaClass 对象
   *
   * @param name 类属性名
   * @author evan Date : 2021/10/11
   */
  public MetaClass metaClassForProperty(String name) {
    Class<?> propType = reflector.getGetterType(name);
    return MetaClass.forClass(propType, reflectorFactory);
  }

  /**
   * 获取属性
   * 比如 order[0].item[0].name
   * 转为 order.item.name
   *
   * @author evan Date : 2021/10/11
   */
  public String findProperty(String name) {
    StringBuilder prop = buildProperty(name, new StringBuilder());
    return prop.length() > 0 ? prop.toString() : null;
  }

  /**
   * 获取属性
   *
   * @author evan Date : 2021/10/11
   */
  public String findProperty(String name, boolean useCamelCaseMapping) {

    // 使用驼峰命名
    if (useCamelCaseMapping) {
      // 将下划线改为""
      name = name.replace("_", "");
    }
    return findProperty(name);
  }

  public String[] getGetterNames() {
    return reflector.getGetablePropertyNames();
  }

  public String[] getSetterNames() {
    return reflector.getSetablePropertyNames();
  }

  /**
   * 获取属性的类型
   *
   * @author evan Date : 2021/10/11
   */
  public Class<?> getSetterType(String name) {

    // 属性分词
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaClass metaProp = metaClassForProperty(prop.getName());

      // 获取最后的类型
      return metaProp.getSetterType(prop.getChildren());
    } else {
      return reflector.getSetterType(prop.getName());
    }
  }

  /**
   * 根据属性名获取 getter 方法的返回类型
   *
   * @author evan Date : 2021/10/11
   */
  public Class<?> getGetterType(String name) {

    // 分词器解析属性
    PropertyTokenizer prop = new PropertyTokenizer(name);

    // 有子表达式
    // 则通过递归的方式获取属性类型
    if (prop.hasNext()) {
      MetaClass metaProp = metaClassForProperty(prop);
      return metaProp.getGetterType(prop.getChildren());
    }

    // 无子表达式

    // issue #506. Resolve the type inside a Collection Object
    return getGetterType(prop);
  }

  private MetaClass metaClassForProperty(PropertyTokenizer prop) {
    Class<?> propType = getGetterType(prop);
    return MetaClass.forClass(propType, reflectorFactory);
  }

  /**
   * 获取 getter 方法的返回类型
   * 处理了返回类型是集合类型的情况
   *
   * @author evan Date : 2021/10/11
   */
  private Class<?> getGetterType(PropertyTokenizer prop) {
    // 根据属性名获取类型
    Class<?> type = reflector.getGetterType(prop.getName());

    // 如果传入的属性表达式包含索引[], 并且属于集合类型
    // 就需要得到其泛型
    if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {

      // 获取返回类型
      Type returnType = getGenericGetterType(prop.getName());

      // 泛型类型
      if (returnType instanceof ParameterizedType) {

        // 泛型对应的真实类型
        Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();

        // 这里判断泛型参数个数为 1
        // 因为 Collection<T> 至多有1 个泛型
        if (actualTypeArguments != null && actualTypeArguments.length == 1) {
          returnType = actualTypeArguments[0];
          if (returnType instanceof Class) {
            type = (Class<?>) returnType;
          } else if (returnType instanceof ParameterizedType) {
            type = (Class<?>) ((ParameterizedType) returnType).getRawType();
          }
        }
      }
    }
    return type;
  }

  private Type getGenericGetterType(String propertyName) {
    try {

      // 根据属性名获取调用者
      Invoker invoker = reflector.getGetInvoker(propertyName);

      // MethodInvoker 说明是 getter 方法
      // 解析method 方法
      // 获取返回类型
      if (invoker instanceof MethodInvoker) {
        Field declaredMethod = MethodInvoker.class.getDeclaredField("method");
        declaredMethod.setAccessible(true);
        Method method = (Method) declaredMethod.get(invoker);
        return TypeParameterResolver.resolveReturnType(method, reflector.getType());
      } else if (invoker instanceof GetFieldInvoker) {

        // GetFieldInvoker 说明是 属性
        // 解析field 对象
        // 获取 field 类型

        Field declaredField = GetFieldInvoker.class.getDeclaredField("field");
        declaredField.setAccessible(true);
        Field field = (Field) declaredField.get(invoker);
        return TypeParameterResolver.resolveFieldType(field, reflector.getType());
      }
    } catch (NoSuchFieldException | IllegalAccessException e) {
      // Ignored
    }
    return null;
  }

  /**
   * 是否有 setter 方法
   * 查询该属性名是否有 setter 方法
   *
   * @param name 属性名
   * @return true 有 setter 方法
   * @author evan Date : 2021/10/12
   */
  public boolean hasSetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      if (reflector.hasSetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop.getName());
        return metaProp.hasSetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasSetter(prop.getName());
    }
  }

  public boolean hasGetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      if (reflector.hasGetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop);
        return metaProp.hasGetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasGetter(prop.getName());
    }
  }

  public Invoker getGetInvoker(String name) {
    return reflector.getGetInvoker(name);
  }

  public Invoker getSetInvoker(String name) {
    return reflector.getSetInvoker(name);
  }

  private StringBuilder buildProperty(String name, StringBuilder builder) {

    // 创建一个属性分词器
    PropertyTokenizer prop = new PropertyTokenizer(name);

    if (prop.hasNext()) {

      // 获取属性名
      // 根据分词器分词, 再通过反射对象查询
      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        // 使用.好拼接属性名
        builder.append(propertyName);
        builder.append(".");

        // 根据属性名获取 metaClass 对象
//        MetaClass metaProp = metaClassForProperty(propertyName);
        // 这里对剩余的字符串进行处理
        // 传入的当前的 stringBuilder

        // TODO: 2021/10/11 这里原来的写法也没什么问题, 自测时使用的无效测试数据导致的问题

        this.buildProperty(prop.getChildren(), builder);
      }
    } else {

      // TODO: 2021/10/11 这里我改为通过分词器的 name 获取属性名, 可以避免最后一个子表达式带有[]的情况

      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

  /**
   * 是否有默认构造方法
   */
  public boolean hasDefaultConstructor() {
    return reflector.hasDefaultConstructor();
  }

}
